package com.datasure.cloudsure.redis.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.datasure.cloudsure.redis.annotation.CacheEvict;
import com.datasure.cloudsure.redis.annotation.CachePut;
import com.datasure.cloudsure.redis.builder.RedisTemplaterFactoryBuild;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;

@Aspect
@Component
public class RedisCacheAop {
    /*spel表达式解析类*/
    private ExpressionParser parser = new SpelExpressionParser();
    /*获取Method中的参数*/
    private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
    /*RedisTemplaterFactoryBuild多数据源工厂*/
    @Autowired
    private RedisTemplaterFactoryBuild redisTemplaterFactoryBuild;

    /*添加缓存动作切点*/
    @Pointcut("@annotation(com.datasure.cloudsure.redis.annotation.CachePut)")
    public void cachePoint() {
    }
    /*剔除缓存动作切点*/
    @Pointcut("@annotation(com.datasure.cloudsure.redis.annotation.CacheEvict)")
    public void flushPoint() {
    }

    /**
     * 添加缓存业务
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("cachePoint()")
    public Object cache(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = null;
        //获取方法
        Method method = this.getMethod(joinPoint);
        CachePut cachePut = (CachePut) method.getAnnotation(CachePut.class);
        String cacheKey = cachePut.cacheKey();
        cacheKey = cacheKey(cacheKey, method, joinPoint);
        RedisTemplate<String, Object> rtw = getRedisTemplaterWrapper(cachePut.dsName());
        //从缓存读
        obj = rtw.opsForValue().get(cacheKey);
        if (null == obj) {
            obj = joinPoint.proceed();
        } else {
            return JSON.parse(obj.toString());
        }
        if (null != obj) {
            rtw.opsForValue().set(cacheKey, JSONObject.toJSONString(obj), cachePut.expire(), cachePut.timeUnit());
        }else {
            //缓存null值，防止缓存击穿
            if (cachePut.cacheNull()) {
                rtw.opsForValue().set(cacheKey, JSONObject.toJSONString(obj), cachePut.cacheNullExpire(), cachePut.timeUnit());
            }
        }
        return obj;
    }

    /**
     * 剔除缓存业务
     * @param joinPoint
     * @throws Throwable
     */
    @After("flushPoint()")
    public void flush(JoinPoint joinPoint) throws Throwable {
        Method method = this.getMethod(joinPoint);
        CacheEvict cacheEvict = (CacheEvict) method.getAnnotation(CacheEvict.class);
        //缓存key
        String cacheKey = cacheEvict.cacheKey();
        cacheKey = cacheKey(cacheKey, method, joinPoint);
        RedisTemplate<String, Object> redisTemplaterWrapper = getRedisTemplaterWrapper(cacheEvict.dsName());
        redisTemplaterWrapper.delete(cacheKey);
    }

    //获取方法
    private Method getMethod(JoinPoint jp) throws Exception {
        MethodSignature msig = (MethodSignature) jp.getSignature();
        Method method = msig.getMethod();
        return method;
    }

    //Spel动态解析cacheKey,获取动态key值
    private String cacheKey(String cacheKey,Method method,JoinPoint joinPoint) {
        if (cacheKey.contains("#")) {
            String[] parameterNames = discoverer.getParameterNames(method);
            StandardEvaluationContext context = new StandardEvaluationContext();
            for (int len = 0; len < parameterNames.length; len++) {
                context.setVariable(parameterNames[len],joinPoint.getArgs()[len]);
            }
            Expression expression = parser.parseExpression(cacheKey);
            cacheKey = expression.getValue(context, String.class);
        }
        return cacheKey;
    }
    /**
     * 动态选择数据源缓存数据
     * 1.如果注解中没有指定数据源，则使用默认数据源
     * 2.如果注解中有指定数据源，则从数据源工厂中获取指定的数据源
     */
    public RedisTemplate<String, Object> getRedisTemplaterWrapper(String dsName) {
        RedisTemplate<String, Object> rtw = redisTemplaterFactoryBuild.getDefaultRedisTemplate();
        if (!StringUtils.isEmpty(dsName)) {
            rtw = redisTemplaterFactoryBuild.getRedisTemplaterByName(dsName);
        }
        return rtw;
    }
}
